patch from arjan to work better with ras files.
authorJonathan Blandford <jrb@src.gnome.org>
Wed, 17 Nov 1999 21:02:33 +0000 (21:02 +0000)
committerJonathan Blandford <jrb@src.gnome.org>
Wed, 17 Nov 1999 21:02:33 +0000 (21:02 +0000)
gdk-pixbuf/io-ras.c

index c45f5bb475f71e867839d544a8c3cf5406f4dd0d..7f44b2820cbc67dd0e1dd15995d1d35da67b6928 100644 (file)
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  */
+
 /*
 
 Known bugs:
-       * "Indexed" (incl grayscale) sunras files don't work
-         ( 1999/11/10 - Fixed for non-progressive loading )
-       * 1 bpp sunrasfiles don't work yet
        * Compressed rasterfiles don't work yet
 
 */
 
 #include <config.h>
 #include <stdio.h>
+#include <unistd.h>
+#include <sys/mman.h>
 #include <glib.h>
 #include <gdk/gdk.h>
 #include <gtk/gtk.h>
@@ -46,6 +45,8 @@ Known bugs:
 /* 
    Header structure for sunras files.
    All values are in big-endian order on disk
+   
+   Note: Every scanline is padded to be a multiple of 16 bits
  */
 
 struct rasterfile {
@@ -82,205 +83,144 @@ static void free_buffer(gpointer user_data, gpointer data)
        free(data);
 }
 
-/*
 
-OneLineBGR_buf/file does what it says: Reads one line from file or buffer.
-Note: It also changes BGR pixelorder to RGB as libart currently
-doesn't support ART_PIX_BGR.
 
-*/
-static void OneLineBGR_buf(guchar * buffer, guint Width, guchar * pixels,
-                          guint bpp)
-{
-       guint X;
 
-       memcpy(pixels, buffer, (size_t) (Width * bpp));
-       X = 0;
-       while (X < Width) {
-               guchar Blue;
-               Blue = pixels[X * bpp];
-               pixels[X * bpp] = pixels[X * bpp + 2];
-               pixels[X * bpp + 2] = Blue;
-               X++;
-       }
-}
+/* Progressive loading */
 
-static void OneLineBGR_file(FILE * f, guint Width, guchar * pixels,
-                           guint bpp)
-{
-       size_t result;
-       guint X;
-       guchar DummyByte;
+struct ras_progressive_state {
+       ModulePreparedNotifyFunc prepared_func;
+       ModuleUpdatedNotifyFunc updated_func;
+       gpointer user_data;
 
-       result = fread(pixels, 1, (size_t) (Width * bpp), f);
+       gint HeaderSize;        /* The size of the header-part (incl colormap) */
+       guchar *HeaderBuf;      /* The buffer for the header (incl colormap) */
+       gint HeaderDone;        /* The nr of bytes actually in HeaderBuf */
 
-       g_assert(result == (size_t) (Width * bpp));
-       if (((Width * bpp) & 1) != 0)   /*  Not 16 bit aligned */
-               (void) fread(&DummyByte, 1, 1, f);
-       X = 0;
-       while (X < Width) {
-               guchar Blue;
-               Blue = pixels[X * bpp];
-               pixels[X * bpp] = pixels[X * bpp + 2];
-               pixels[X * bpp + 2] = Blue;
-               X++;
-       }
-}
+       gint LineWidth;         /* The width of a line in bytes */
+       guchar *LineBuf;        /* Buffer for 1 line */
+       gint LineDone;          /* # of bytes in LineBuf */
+       gint Lines;             /* # of finished lines */
 
-static void OneLineMapped_file(FILE * f, guint Width, guchar * pixels,
-                           guint bpp, guchar *Map)
-{
-       size_t result;
-       guint X;
-       guchar DummyByte;
-       guchar *buffer;
-       
-       
-       buffer =g_malloc((size_t) (Width * bpp));
-       
-       g_assert(buffer!=NULL);
+       gint RasType;           /*  32 = BGRA
+                                  24 = BGR
+                                  8 = 8 bit colormapped
+                                  1  = 1 bit bitonal 
+                                */
 
-       result = fread(buffer, 1, (size_t) (Width * bpp), f);
 
-       g_assert(result == (size_t) (Width * bpp));
-       
-       if (((Width * bpp) & 1) != 0)   /*  Not 16 bit aligned */
-               (void) fread(&DummyByte, 1, 1, f);
-       X = 0;
-       while (X < Width) {
-               pixels[X * 3]   = Map[buffer[X]];
-               pixels[X * 3+1] = Map[buffer[X]+256];
-               pixels[X * 3+2] = Map[buffer[X]+512];
+       struct rasterfile Header;       /* Decoded (BE->CPU) header */
 
-               X++;
-       }
-       g_free(buffer);
-}
 
-static void OneLineMapped_buf(guchar *buffer, guint Width, guchar * pixels,
-                           guint bpp,guchar *Map)
-{
-       size_t result;
-       guint X;
-       
-       X = 0;
-       while (X < Width) {
-               pixels[X * 3]   = Map[buffer[X]*3];
-               pixels[X * 3+1] = Map[buffer[X]*3+1];
-               pixels[X * 3+2] = Map[buffer[X]*3+2];
+       GdkPixbuf *pixbuf;      /* Our "target" */
+};
+
+gpointer
+image_begin_load(ModulePreparedNotifyFunc prepared_func,
+                ModuleUpdatedNotifyFunc updated_func, gpointer user_data);
+void image_stop_load(gpointer data);
+gboolean image_load_increment(gpointer data, guchar * buf, guint size);
+
 
-               X++;
-       }
-}
 
 /* Shared library entry point */
 GdkPixbuf *image_load(FILE * f)
 {
-       guint bpp;
-       guint Y;
-       size_t i;
-       guchar *pixels;
-       guchar ColorMap[768];
-       struct rasterfile Header;
-
-       i = fread(&Header, 1, sizeof(Header), f);
-       g_assert(i == 32);
+       guchar *membuf;
+       size_t length;
+       struct ras_progressive_state *State;
+       int fd;
        
-       /* Fill default colormap */
-       for (Y=0;Y<256;Y++) {
-               ColorMap[Y*3+0]=Y;
-               ColorMap[Y*3+1]=Y;
-               ColorMap[Y*3+2]=Y;
-       }
-       /* Correct the byteorder of the header here */
-       Header.width = be32_to_cpu(Header.width);
-       Header.height = be32_to_cpu(Header.height);
-       Header.depth = be32_to_cpu(Header.depth);
-       Header.length = be32_to_cpu(Header.length);
-       Header.type = be32_to_cpu(Header.type);
-       Header.maptype = be32_to_cpu(Header.maptype);
-       Header.maplength = be32_to_cpu(Header.maplength);
-
-
-       bpp = 0;
-       if (Header.depth == 32)
-               bpp = 4;
-       if (Header.depth == 24)
-               bpp = 3;
-       if (Header.depth == 8)
-               bpp = 1;
-               
-       printf("mapl %i \n",Header.maplength);
-       if (Header.maplength>0)
-               i = fread(ColorMap,1,Header.maplength,f);
-                               
-
-       g_assert(bpp != 0);     /* Only 24 and 32 bpp for now */
-
-       if (bpp==4)
-               pixels = (guchar *) g_malloc(Header.width * Header.height * 4);
-       else
-               pixels = (guchar *) g_malloc(Header.width * Header.height * 3);
-       if (!pixels) {
-               return NULL;
-       }
-
-       /* 
-
-          Loop through the file, one line at a time. 
-          Only BGR-style files are handled right now.
+       GdkPixbuf *pb;
+       
+       State = image_begin_load(NULL, NULL, NULL);
+       
+       membuf = g_malloc(4096);
+       
+       g_assert(membuf != NULL);
+       
+       while (feof(f) == 0) {
+               length = fread(membuf, 1, 4096, f);
+               image_load_increment(State, membuf, length);
+       } 
+       g_free(membuf);
+       if (State->pixbuf != NULL)
+               gdk_pixbuf_ref(State->pixbuf);
+
+       pb = State->pixbuf;
+
+       image_stop_load(State);
+       return State->pixbuf;
+}
 
-        */
-       Y = 0;
-       while (Y < Header.height) {
-               if (bpp>1) 
-                       OneLineBGR_file(f, Header.width,
-                               &pixels[Y * Header.width * bpp], bpp);
-               else
-                       OneLineMapped_file(f, Header.width,
-                               &pixels[Y * Header.width * 3], bpp,&ColorMap[0]);
-               Y++;
+static void RAS2State(struct rasterfile *RAS,
+                     struct ras_progressive_state *State)
+{
+       State->Header.width = be32_to_cpu(RAS->width);
+       State->Header.height = be32_to_cpu(RAS->height);
+       State->Header.depth = be32_to_cpu(RAS->depth);
+       State->Header.type = be32_to_cpu(RAS->type);
+       State->Header.maptype = be32_to_cpu(RAS->maptype);
+       State->Header.maplength = be32_to_cpu(RAS->maplength);
+
+       g_assert(State->Header.maplength <= 768);       /* Otherwise, we are in trouble */
+
+       State->RasType = State->Header.depth;   /* This may be less trivial someday */
+       State->HeaderSize = 32 + State->Header.maplength;
+
+       if (State->RasType == 32)
+               State->LineWidth = State->Header.width * 4;
+       if (State->RasType == 24)
+               State->LineWidth = State->Header.width * 3;
+       if (State->RasType == 8)
+               State->LineWidth = State->Header.width * 1;
+       if (State->RasType == 1) {
+               State->LineWidth = State->Header.width / 8;
+               if ((State->Header.width & 7) != 0)
+                       State->LineWidth++;
        }
 
+       /* Now padd the line to be a multiple of 16 bits */
+       if ((State->LineWidth & 1) != 0)
+               State->LineWidth++;
 
-       if (bpp == 4)
-               return gdk_pixbuf_new_from_data(pixels, ART_PIX_RGB, TRUE,
-                                               (gint) Header.width,
-                                               (gint) Header.height,
-                                               (gint) (Header.width *
-                                                       4), free_buffer,
-                                               NULL);
-       else
-               return gdk_pixbuf_new_from_data(pixels, ART_PIX_RGB, FALSE,
-                                               (gint) Header.width,
-                                               (gint) Header.height,
-                                               (gint) (Header.width *
-                                                       3), free_buffer,
-                                               NULL);
-}
+       if (State->LineBuf == NULL)
+               State->LineBuf = g_malloc(State->LineWidth);
 
+       g_assert(State->LineBuf != NULL);
 
-/* Progressive loading */
-
-struct ras_progressive_state {
-       ModulePreparedNotifyFunc prepared_func;
-       ModuleUpdatedNotifyFunc updated_func;
-       gpointer user_data;
-
-       GdkPixbuf *pixbuf;      /* Our "target" */
-       guchar *PixelData;      /* pointer to the next line */
-       struct rasterfile Header;
-       guint bpp;
-       guint BytesPerLine;
-       guchar *linebuf;
-       guchar *ColorMap;
-       guint BytesInLB;
-       guint LinesDone;
 
+       if (State->pixbuf == NULL) {
+               if (State->RasType == 32)
+                       State->pixbuf = gdk_pixbuf_new(ART_PIX_RGB, TRUE,
+                                                      8,
+                                                      (gint)
+                                                      State->Header.width,
+                                                      (gint)
+                                                      State->Header.
+                                                      height);
+               else
+                       State->pixbuf =
+                           gdk_pixbuf_new(ART_PIX_RGB, FALSE, 8,
+                                          (gint) State->Header.width,
+                                          (gint) State->Header.height);
+               if (State->prepared_func != NULL)
+                       /* Notify the client that we are ready to go */
+                       (*State->prepared_func) (State->pixbuf,
+                                                State->user_data);
 
+       }
+       
+       if ((State->Header.maplength==0)&&(State->RasType==1)) {
+               State->HeaderBuf[32] = 255;
+               State->HeaderBuf[33] = 0;
+               State->HeaderBuf[34] = 255;
+               State->HeaderBuf[35] = 0;
+               State->HeaderBuf[36] = 255;
+               State->HeaderBuf[37] = 0;
+       }
 
-       gboolean have_header;
-};
+}
 
 /* 
  * func - called when we have pixmap created (but no image data)
@@ -295,18 +235,27 @@ image_begin_load(ModulePreparedNotifyFunc prepared_func,
        struct ras_progressive_state *context;
 
        context = g_new0(struct ras_progressive_state, 1);
-
        context->prepared_func = prepared_func;
        context->updated_func = updated_func;
        context->user_data = user_data;
+
+       context->HeaderSize = 32;
+       context->HeaderBuf = g_malloc(32 + 768);        /* 32 for rasheader,
+                                                          768 for the colormap */
+       context->HeaderDone = 0;
+
+       context->LineWidth = 0;
+       context->LineBuf = NULL;
+       context->LineDone = 0;
+       context->Lines = 0;
+
+       context->RasType = 0;
+
+       memset(&context->Header, 0, sizeof(struct rasterfile));
+
+
        context->pixbuf = NULL;
-       context->linebuf = NULL;
-       context->bpp = 0;
-       context->have_header = 0;
-       context->BytesInLB = 0;
-       context->BytesPerLine = 0;
-       context->LinesDone = 0;
-       context->ColorMap = NULL;
+
 
        return (gpointer) context;
 }
@@ -321,18 +270,132 @@ void image_stop_load(gpointer data)
        struct ras_progressive_state *context =
            (struct ras_progressive_state *) data;
 
+
        g_return_if_fail(context != NULL);
 
+       if (context->LineBuf != NULL)
+               g_free(context->LineBuf);
+       if (context->HeaderBuf != NULL)
+               g_free(context->HeaderBuf);
+
        if (context->pixbuf)
                gdk_pixbuf_unref(context->pixbuf);
 
-       if (context->linebuf)
-               g_free(context->linebuf);
-       if (context->ColorMap)
-               g_free(context->ColorMap);
        g_free(context);
 }
 
+/* 
+ OneLine is called when enough data is received to process 1 line 
+ of pixels 
+ */
+
+static void OneLine32(struct ras_progressive_state *context)
+{
+       gint X;
+       guchar *Pixels;
+
+       X = 0;
+       Pixels = context->pixbuf->art_pixbuf->pixels +
+           context->pixbuf->art_pixbuf->rowstride * context->Lines;
+       while (X < context->Header.width) {
+               /* The joys of having a BGR byteorder */
+               Pixels[X * 4 + 0] = context->LineBuf[X * 4 + 2];
+               Pixels[X * 4 + 1] = context->LineBuf[X * 4 + 1];
+               Pixels[X * 4 + 2] = context->LineBuf[X * 4 + 0];
+               Pixels[X * 4 + 3] = context->LineBuf[X * 4 + 3];
+               X++;
+       }
+}
+
+static void OneLine24(struct ras_progressive_state *context)
+{
+       gint X;
+       guchar *Pixels;
+
+       X = 0;
+       Pixels = context->pixbuf->art_pixbuf->pixels +
+           context->pixbuf->art_pixbuf->rowstride * context->Lines;
+       while (X < context->Header.width) {
+               /* The joys of having a BGR byteorder */
+               Pixels[X * 3 + 0] = context->LineBuf[X * 3 + 2];
+               Pixels[X * 3 + 1] = context->LineBuf[X * 3 + 1];
+               Pixels[X * 3 + 2] = context->LineBuf[X * 3 + 0];
+               X++;
+       }
+
+}
+
+static void OneLine8(struct ras_progressive_state *context)
+{
+       gint X;
+       guchar *Pixels;
+
+       X = 0;
+       Pixels = context->pixbuf->art_pixbuf->pixels +
+           context->pixbuf->art_pixbuf->rowstride * context->Lines;
+       while (X < context->Header.width) {
+               /* The joys of having a BGR byteorder */
+               Pixels[X * 3 + 0] =
+                   context->HeaderBuf[context->LineBuf[X] + 32];
+               Pixels[X * 3 + 1] =
+                   context->HeaderBuf[context->LineBuf[X] + 256 + 32];
+               Pixels[X * 3 + 2] =
+                   context->HeaderBuf[context->LineBuf[X] + 512 + 32];
+               X++;
+       }
+}
+
+static void OneLine1(struct ras_progressive_state *context)
+{
+       gint X;
+       guchar *Pixels;
+
+       X = 0;
+       Pixels = context->pixbuf->art_pixbuf->pixels +
+           context->pixbuf->art_pixbuf->rowstride * context->Lines;
+       while (X < context->Header.width) {
+               int Bit;
+               
+               Bit = (context->LineBuf[X/8])>>(7-(X&7));
+               Bit = Bit & 1;
+               /* The joys of having a BGR byteorder */
+               Pixels[X * 3 + 0] =
+                   context->HeaderBuf[Bit + 32];
+               Pixels[X * 3 + 1] =
+                   context->HeaderBuf[Bit + 2 + 32];
+               Pixels[X * 3 + 2] =
+                   context->HeaderBuf[Bit + 4 + 32];
+               X++;
+       }
+}
+
+
+static void OneLine(struct ras_progressive_state *context)
+{
+       if (context->RasType == 32)
+               OneLine32(context);
+       if (context->RasType == 24)
+               OneLine24(context);
+       if (context->RasType == 8)
+               OneLine8(context);
+       if (context->RasType == 1)
+               OneLine1(context);
+
+       context->LineDone = 0;
+       if (context->Lines > context->Header.height)
+               return;
+       context->Lines++;
+
+       if (context->updated_func != NULL) {
+               (*context->updated_func) (context->pixbuf,
+                                         context->user_data,
+                                         0,
+                                         context->Lines,
+                                         context->Header.width,
+                                         context->Header.height);
+
+       }
+}
 
 /*
  * context - from image_begin_load
@@ -346,117 +409,52 @@ gboolean image_load_increment(gpointer data, guchar * buf, guint size)
        struct ras_progressive_state *context =
            (struct ras_progressive_state *) data;
 
-       if ((context->have_header == 0) && (size < 32))
-               return FALSE;
-
-       if (context->have_header == FALSE) {
-               memcpy(&context->Header, buf, 32);
-               buf += 32;
-               size -= 32;
-
-               context->Header.width = be32_to_cpu(context->Header.width);
-               context->Header.height =
-                   be32_to_cpu(context->Header.height);
-               context->Header.depth = be32_to_cpu(context->Header.depth);
-               context->Header.length =
-                   be32_to_cpu(context->Header.length);
-               context->Header.type = be32_to_cpu(context->Header.type);
-               context->Header.maptype =
-                   be32_to_cpu(context->Header.maptype);
-               context->Header.maplength =
-                   be32_to_cpu(context->Header.maplength);
-
-               context->bpp = 0;
-               if (context->Header.depth == 32)
-                       context->bpp = 4;
-               if (context->Header.depth == 24)
-                       context->bpp = 3;
-               if (context->Header.depth == 8)
-                       context->bpp = 1;
-                       
-
-               g_assert(context->bpp != 0);    /* Only 24 and 32 bpp for now */
-
-
-               if (context->bpp == 4)
-                       context->pixbuf = gdk_pixbuf_new(ART_PIX_RGB, TRUE,
-                                                        8,
-                                                        (gint)
-                                                        context->Header.
-                                                        width,
-                                                        (gint)
-                                                        context->Header.
-                                                        height);
-               else
-                       context->pixbuf =
-                           gdk_pixbuf_new(ART_PIX_RGB, FALSE, 8,
-                                          (gint) context->Header.width,
-                                          (gint) context->Header.height);
-
-               /* Use pixbuf buffer to store decompressed data */
-               g_assert(context->pixbuf != NULL);
-               g_assert(context->pixbuf->art_pixbuf != NULL);
-               context->PixelData = context->pixbuf->art_pixbuf->pixels;
-               g_assert(context->PixelData != NULL);
-               context->BytesPerLine =
-                   context->Header.width * context->bpp;
-
-               if (((context->BytesPerLine) & 1) != 0) /*  Not 16 bit aligned */
-                       context->BytesPerLine++;
-
-               context->linebuf =
-                   (guchar *) g_malloc(context->BytesPerLine);
-               context->have_header = TRUE;
-               if (context->linebuf == NULL) {
-                       /* Failed to allocate memory */
-                       g_error("Couldn't allocate linebuf");
-               }
-
-
-               /* Notify the client that we are ready to go */
-               (*context->prepared_func) (context->pixbuf,
-                                          context->user_data);
-       }
-
+       gint BytesToCopy;
 
        while (size > 0) {
-               guint ToDo;
-               ToDo = context->BytesPerLine - context->BytesInLB;
-               if (ToDo > size)
-                       ToDo = size;
-               memcpy(context->linebuf + context->BytesInLB,
-                      buf, (size_t) ToDo);
-               size -= ToDo;
-               buf += ToDo;
-               context->BytesInLB += ToDo;
-               if (context->BytesInLB >= context->BytesPerLine) {
-                       /* linebuf if full */
-                       if (context->bpp>1)
-                               OneLineBGR_buf(context->linebuf,
-                                      context->Header.width,
-                                      context->PixelData, context->bpp);
-                       else                                 
-                               OneLineMapped_buf(context->linebuf,
-                                      context->Header.width,
-                                      context->PixelData, context->bpp,
-                                      context->ColorMap);
-                       context->BytesInLB = 0;
-                       context->PixelData +=
-                           context->pixbuf->art_pixbuf->rowstride;
-                       context->LinesDone++;
-
-                       (*context->updated_func) (context->pixbuf,
-                                                 context->user_data,
-                                                 0,
-                                                 context->LinesDone,
-                                                 context->Header.width,
-                                                 context->Header.height);
+               if (context->HeaderDone < context->HeaderSize) {        /* We still 
+                                                                          have headerbytes to do */
+                       BytesToCopy =
+                           context->HeaderSize - context->HeaderDone;
+                       if (BytesToCopy > size)
+                               BytesToCopy = size;
+
+                       memcpy(context->HeaderBuf + context->HeaderDone,
+                              buf, BytesToCopy);
+
+                       size -= BytesToCopy;
+                       buf += BytesToCopy;
+                       context->HeaderDone += BytesToCopy;
+
+               } else {
+                       /* Pixeldata only */
+                       BytesToCopy =
+                           context->LineWidth - context->LineDone;
+                       if (BytesToCopy > size)
+                               BytesToCopy = size;
+
+                       if (BytesToCopy > 0) {
+                               memcpy(context->LineBuf +
+                                      context->LineDone, buf,
+                                      BytesToCopy);
+
+                               size -= BytesToCopy;
+                               buf += BytesToCopy;
+                               context->LineDone += BytesToCopy;
+                       }
+                       if ((context->LineDone >= context->LineWidth) &&
+                           (context->LineWidth > 0))
+                               OneLine(context);
 
 
                }
 
+               if (context->HeaderDone >= 32)
+                       RAS2State((struct rasterfile *) context->HeaderBuf,
+                                 context);
+
+
        }
 
        return TRUE;
 }
-